package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"math"
	"time"
)

type GnssSVRinex struct {
	decodestate          int8
	rinexHeader          *RinexHeaderEx
	StartTime_OBS        time.Time
	cachelist            *utils.ArrayList
	FixInterval          int64 // 固定采样间隔 单位：秒  5s,  mod(t, 5) = 0
	X, Y, Z              float64
	HeadCacheNum         int
	CopyRight            string
	SoftVersion          string
	OutputPhVWhenLockIs0 int8 // 1:输出(默认)
	DopplerNegative      int8 // 0:不输出负数, 1:在doppler前面加负号
	RinexOutputFn        func(epoch *GnssSVEpoch, str string)
	OnInitialFunc        func(head *RinexHeaderEx)
	OnGetTimeFunc        func(epoch *GnssSVEpoch) time.Time
	CheckSignal          func(sig *GnssSVSignal) bool
}

func NewGnssSVRinex() *GnssSVRinex {
	rval := &GnssSVRinex{
		decodestate:          -1,
		HeadCacheNum:         30,
		OutputPhVWhenLockIs0: 0,
	}
	return rval
}

func (this *GnssSVRinex) GetNow(epoch *GnssSVEpoch) time.Time {
	if this.OnGetTimeFunc != nil {
		return this.OnGetTimeFunc(epoch)
	}
	return time.Now()
}

func (this *GnssSVRinex) checkInterval(epoch *GnssSVEpoch) bool {
	if this.FixInterval > 0 {
		f_msec := float64(epoch.TOW)
		i_msec := int64(math.Floor(f_msec + 0.1))
		v := i_msec % (this.FixInterval * 1000)
		if v != 0 {
			return false
		} else {
			return true
		}
	}
	return true
}

func (this *GnssSVRinex) GetRinexHeader() string {
	// first line
	str := PrintRinexFV(3.04, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
	str = utils.AddSuffixForWidth(str, 60, " ")
	str += "RINEX VERSION / TYPE\n"

	str += utils.AddSuffixForWidth(this.SoftVersion, 20, " ") +
		utils.AddSuffixForWidth(this.CopyRight, 20, " ") +
		utils.AddSuffixForWidth(time.Now().UTC().Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	xyzstr := utils.FormatFloatVlst("%.4f", 14, 60, this.X, this.Y, this.Z)
	str += fmt.Sprintf("%sAPPROX POSITION XYZ\n", xyzstr)
	str += this.rinexHeader.PrintSignHeader()
	if !this.StartTime_OBS.IsZero() {
		str += "  " + this.StartTime_OBS.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n"
	}
	str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"
	return str
}

//func (this *GnssSVRinex) innerOutputHeader() {
//	this.innerOutputRinex(this.GetRinexHeader())
//}

func (this *GnssSVRinex) innerOutputRinex(epoch *GnssSVEpoch, data string) {
	if this.RinexOutputFn != nil {
		this.RinexOutputFn(epoch, data)
	}
}

func (this *GnssSVRinex) StatusString() string {
	if this.cachelist == nil {
		return fmt.Sprintf("cache-n:%d", 0)
	}
	return fmt.Sprintf("cache-n:%d", this.cachelist.Count())
}

func (this *GnssSVRinex) innerAppendLLI(sb *utils.BytesBuilder, sig *GnssSVSignal) {
	lli := sig.GetRnxLLI()
	if lli == 0 {
		sb.AppendStr("  ")
	} else {
		sb.Appendf("%d ", lli) //LLI SSI
	}
}

func (this *GnssSVRinex) innerWriteSignal(sb *utils.BytesBuilder, sig *GnssSVSignal) {
	if sig != nil {
		sb.AppendStr(PrintRinexFV(sig.PsV, 3, 14))
		sb.AppendStr("  ") //LLI SSI

		if this.OutputPhVWhenLockIs0 == 0 && sig.LkT == 0 { // 空值
			sb.AppendStr(PrintRinexFV(0, 3, 14))
		} else {
			sb.AppendStr(PrintRinexFV(sig.PhV, 3, 14))
		}
		this.innerAppendLLI(sb, sig)

		dop := sig.Doppler
		if this.DopplerNegative == 1 {
			dop = -dop
		}
		sb.AppendStr(PrintRinexFV(dop, 3, 14))
		sb.AppendStr("  ") //LLI SSI

		sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 14))
		sb.AppendStr("  ") //LLI SSI
	} else {
		sb.AppendStr(PrintRinexFV(0, 3, 14))
		sb.AppendStr("  ") //LLI SSI

		sb.AppendStr(PrintRinexFV(0, 3, 14))
		sb.AppendStr("  ") //LLI SSI

		sb.AppendStr(PrintRinexFV(0, 3, 14))
		sb.AppendStr("  ") //LLI SSI

		sb.AppendStr(PrintRinexFV(0, 3, 14))
		sb.AppendStr("  ") //LLI SSI
	}
}

func (this *GnssSVRinex) innerProcessObs(epoch *GnssSVEpoch) {
	var sb utils.BytesBuilder
	n := 0
	/*
		Epoch flag = 0 or 1: OBSERVATION records follow
		− Satellite number (see section 4.5)    A1,I2.2
		− m fields of observation data (in the same sequence as given in the   m(F14.3,
		respective SYS / # / OBS TYPES header record), each containing the
		specified observations for example: pseudorange, phase, Doppler and SNR.
		− Loss of Lock Indicator - LLI (see Note 1)   I1,
		− Signal Strength Indicator - SSI (see Note 2) I1)
	*/
	if len(this.rinexHeader.CHeader) > 0 {
		epoch.RangeSys(SYS_BD, func(obs *GnssSVObs) bool {
			n++
			prnCode := obs.GetPrnCode()
			sb.AppendStr(prnCode)
			for i := 0; i < len(this.rinexHeader.CHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.CHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if len(this.rinexHeader.GHeader) > 0 {
		epoch.RangeSys(SYS_GPS, func(obs *GnssSVObs) bool {
			n++
			sb.AppendStr(obs.GetPrnCode())
			for i := 0; i < len(this.rinexHeader.GHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.GHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if len(this.rinexHeader.GloHeader) > 0 {
		epoch.RangeSys(SYS_GLO, func(obs *GnssSVObs) bool {
			n++
			sb.AppendStr(obs.GetPrnCode())
			for i := 0; i < len(this.rinexHeader.GloHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.GloHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if len(this.rinexHeader.EHeader) > 0 {
		epoch.RangeSys(SYS_GAL, func(obs *GnssSVObs) bool {
			n++
			sb.AppendStr(obs.GetPrnCode())
			for i := 0; i < len(this.rinexHeader.EHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.EHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if len(this.rinexHeader.JHeader) > 0 {
		epoch.RangeSys(SYS_QZS, func(obs *GnssSVObs) bool {
			n++
			sb.AppendStr(obs.GetPrnCode())
			for i := 0; i < len(this.rinexHeader.JHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.JHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if len(this.rinexHeader.WlnavHeader) > 0 {
		epoch.RangeSys(SYS_WLNAV, func(obs *GnssSVObs) bool {
			n++
			sb.AppendStr(obs.GetPrnCode())
			for i := 0; i < len(this.rinexHeader.WlnavHeader); i++ {
				sig := obs.GetSingal(this.rinexHeader.WlnavHeader[i])
				this.innerWriteSignal(&sb, sig)
			}
			sb.AppendStr("\n")
			return true
		})
	}

	if n > 0 {
		var header utils.BytesBuilder
		header.AppendStrs("> ", epoch.GetGpsTime(this.GetNow(epoch)).Format("2006 01 02 15 04 05.0000000")+"  0 "+fmt.Sprintf("%.2d", n)).AppendStr("\n")
		this.innerOutputRinex(epoch, header.String())
		this.innerOutputRinex(epoch, sb.String())
	}
}

func (this *GnssSVRinex) ProcessCache() {
	if this.cachelist == nil {
		return
	}
	this.cachelist.Range(func(val interface{}) bool {
		if this.checkInterval(val.(*GnssSVEpoch)) {
			this.innerProcessObs(val.(*GnssSVEpoch))
		}
		//this.innerProcessObs(val.(*GnssSVEpoch))
		return true
	})
	this.cachelist = utils.NewArrayList(24)
}

func (this *GnssSVRinex) InputEpoch(epoch *GnssSVEpoch) {
	if this.decodestate <= 0 {
		this.cachelist = utils.NewArrayList(24)
		this.rinexHeader = NewRinexHeaderEx()
		if this.OnInitialFunc != nil {
			this.OnInitialFunc(this.rinexHeader)
		}
		this.decodestate = 1
	}

	if this.decodestate == 1 {
		epoch.Range(func(navisys byte, obs *GnssSVObs) bool {
			obs.RangeSignal(func(signal *GnssSVSignal) bool {
				if this.CheckSignal == nil || this.CheckSignal(signal) {
					this.rinexHeader.CheckAddSign(signal.NaviSys, GnssSvSigId2Code(signal.NaviSys, signal.SigId))
				}
				return true
			})
			return true
		})
		//if !this.checkInterval(epoch) {
		//	return
		//}
		this.cachelist.Append(epoch.Clone()) // clone入队列

		if this.cachelist.Count() > this.HeadCacheNum {
			// this.innerOutputHeader()
			this.ProcessCache()
			this.decodestate = 2
		}
	} else {
		if !this.checkInterval(epoch) {
			return
		}
		this.innerProcessObs(epoch)
	}
}
